import sys
import json
import time
import threading
import open3d as o3d
from flask_cors import CORS
from lidar_simulation import LidarSimulation
from deal_data import DealData
from util import *
import requests
from flask import Flask, request, jsonify

app = Flask(__name__)
CORS(app, resources={r'/*': {"origins": "*"}})

points_low = None
points_mid = None
points_high = None
config_json = None
count = 0
lidar_lines = 0


def make_response(data, status_code):
    # 在使用jsonify之前确保有应用上下文
    # with app.app_context():
    response = jsonify(data)
    response.status_code = status_code
    return response


def http_post(url, data):
    try:
        # 发起POST请求
        headers = {'Content-Type': 'application/json'}
        response = requests.post(url, json=data, headers=headers)
        # 检查响应状态码
        if response.status_code == 200:
            # 请求成功，返回响应内容
            return response.text
        else:
            # 请求失败，打印错误信息
            print("POST请求失败：", response.status_code)
            return None
    except Exception as e:
        # 发生异常，打印异常信息
        print("发生异常：", e)
        return None


def http_get(url):
    try:
        # 发起GET请求
        response = requests.get(url)
        # 检查响应状态码
        if response.status_code == 200:
            # 请求成功，返回响应内容
            return response.text
        else:
            # 请求失败，打印错误信息
            print("POST请求失败：", response.status_code)
            return None
    except Exception as e:
        # 发生异常，打印异常信息
        print("发生异常：", e)
        return None


def send_http(url, path):
    # 要上传的文件
    files = {'file': open(path, 'rb')}
    # 其他表单数据
    data = {
        "type": "lidar"
    }
    # 发送POST请求上传文件
    response = requests.post(url, files=files, data=data)
    # 检查响应状态码
    if response.status_code == 200:
        print("文件上传成功")
    else:
        print("文件上传失败:", response.status_code)


def load_config(path):
    global config_json
    encoding_ = detect_encoding(path)
    with open(path, 'r', encoding=str(encoding_)) as f:
        config_json = json.load(f)
        print(config_json)


def load_pcd(pcd_path):
    global points_low, points_mid, points_high
    # open3d解析本地pcd文件
    low_pcd_file = os.path.join(pcd_path, 'low')
    mid_pcd_file = os.path.join(pcd_path, 'medium')
    high_pcd_file = os.path.join(pcd_path, 'high')
    try:
        low_file_path = find_frist_pcd_or_ply(low_pcd_file)
        if low_file_path is None:
            print("没有找到低密度点云文件")
            return
        else:
            pcd_low = o3d.io.read_point_cloud(low_file_path)
            points_low = np.asarray(pcd_low.points)
            print(points_low.shape)
            print("加载点云成功！！！！")
            # 返回加载成功的信息 "http://10.100.12.40:8085/scenlist/loadpcdsuccess"
            url_load = 'http://' + str(config_json['host']) + ':' + str(config_json['web_prot']) + '/scenlist/loadpcdsuccess'
            http_get(url_load)
    except Exception as e:
        print("加载低密度点云失败！！！！", e)
    try:
        mid_pcd_path = find_frist_pcd_or_ply(mid_pcd_file)
        if mid_pcd_path is None:
            print("没有找到中密度点云文件")
            return
        else:
            pcd_mid = o3d.io.read_point_cloud(mid_pcd_path)
            points_mid = np.asarray(pcd_mid.points)
            print("加载中密度点云成功！！！！")
    except Exception as e:
        print("加载中密度点云失败！！！！", e)
    try:
        high_pcd_path = find_frist_pcd_or_ply(high_pcd_file)
        if high_pcd_path is None:
            print("没有找到高密度点云文件")
            return
        else:
            pcd_high = o3d.io.read_point_cloud(high_pcd_path)
            points_high = np.asarray(pcd_high.points)
            print("加载高密度点云成功！！！！")
    except Exception as e:
        print("加载高密度点云失败！！！！", e)


def get_count(lidar_sim):
    global count
    flag = True
    while flag:
        time.sleep(0.01)
        count = lidar_sim.get_count()
        flag = lidar_sim.get_run_flag()


def run(Params):

    pcd_points = None
    save_path = ""
    if Params['pcd_level'] == 0:
        pcd_points = points_low
        save_path = os.path.join(Params['save_folder'], Params['pole_number'], 'low.pcd')
    elif Params['pcd_level'] == 1:
        pcd_points = points_mid
        save_path = os.path.join(Params['save_folder'], Params['pole_number'], 'mid.pcd')
    elif Params['pcd_level'] == 2:
        pcd_points = points_high
        save_path = os.path.join(Params['save_folder'], Params['pole_number'], 'high.pcd')

    parent_dir = os.path.dirname(save_path)
    if not os.path.exists(parent_dir):
        os.makedirs(parent_dir)

    if pcd_points is not None:
        conf_dict = {
            'pcd_points': pcd_points,
            'save_path': save_path,
            'origin_position': Params['origin_position'],
            'light_pole_position': Params['light_pole_position'],
            'lidar_type': Params['lidar_type'],
            'lidar_resolution': Params['lidar_resolution'],
            'lidar_range': Params['lidar_range'],
            'lidar_list': Params['lidar_list'],
            'pcd_level': Params['pcd_level'],
            'pole_radius': Params['pole_radius']
        }
        # 创建仿真对象
        lidar_sim = LidarSimulation(conf_dict)
        # 获取仿真进度（完成圈数）
        threading.Thread(target=get_count, args=(lidar_sim,)).start()
        # 开始仿真
        lidar_sim.run()
        # 获取仿真的雷达点云数据
        all_data = lidar_sim.get_all_data()
        # 计算路灯杆遮挡造成的盲区
        pole_bind_angle = calculate_angle(Params['origin_position'], Params['light_pole_position'], Params['pole_radius'])
        # 创建数据处理对象
        data_deal = DealData(all_data, Params['lidar_resolution'], config_json['blind_area'], pole_bind_angle)
        # 处理数据
        data_deal.run()
        # 获取处理后的数据列表
        data_list = data_deal.get_position_data()

        # 调用完成接口 http://10.100.12.40:8085/updatePCD
        url_PCD = 'http://' + str(config_json['host']) + ':' + str(config_json['web_prot']) + '/lidarPCD'
        data_PCD = {
            "save_path": save_path
        }
        http_post(url_PCD, data_PCD)
        print("上传点云数据路径成功，路径为：", save_path)

        # 调用完成接口 http://10.100.12.40:8085/lidarBlindArea
        url_area = 'http://' + str(config_json['host']) + ':' + str(config_json['web_prot']) + '/lidarBlindArea'
        data_area = {
            "data": data_list
        }
        http_post(url_area, data_area)
        print("上传盲区数据成功，盲区边界向量列表为：", data_list)

        # send_http(url_pcd, save_path)

        # # 检查临时文件是否存在，如果存在则删除
        # delete_file(save_path)
    else:
        print("没有点云数据")


@app.route('/setFile', methods=['Post'])
def setFile():
    global points_low, points_mid, points_high
    points_low = None
    points_mid = None
    points_high = None
    sim_cfg = request.get_json()
    assert 'file' in sim_cfg.keys(), 'The file path cannot be empty.'
    assert 'pcdIn' in sim_cfg.keys(), 'The path cannot be empty.'

    ply_path = os.path.join(sim_cfg['pcdIn'], sim_cfg['file'])
    # 加载ply文件，并保存点云数据。
    p = threading.Thread(target=load_pcd, args=(ply_path,))
    p.start()
    # print('开始加载点云文件', points)

    return make_response('setFile OK', 200)


@app.route('/setPosition', methods=['Post'])
def setPosition():
    global count, lidar_lines, config_json
    count = 0
    # 重新读取配置参数
    load_config(config_path)
    lidar_cfg = request.get_json()
    assert 'x' in lidar_cfg.keys(), 'Lidar X value cannot be empty.'
    assert 'y' in lidar_cfg.keys(), 'Lidar Y value cannot be empty.'
    assert 'z' in lidar_cfg.keys(), 'Lidar Z value cannot be empty.'
    assert 'level' in lidar_cfg.keys(), 'Point cloud level value cannot be empty.'
    assert 'lidar_type' in lidar_cfg.keys(), 'Lidar type value cannot be empty.'
    assert 'lidar_resolution' in lidar_cfg.keys(), 'Lidar resolution value cannot be empty.'
    assert 'lidar_range' in lidar_cfg.keys(), 'Lidar range value cannot be empty.'
    assert 'pole_x' in lidar_cfg.keys(), 'light_pole X value cannot be empty.'
    assert 'pole_y' in lidar_cfg.keys(), 'light_pole Y value cannot be empty.'
    assert 'pole_z' in lidar_cfg.keys(), 'light_pole Z value cannot be empty.'
    assert 'pole_radius' in lidar_cfg.keys(), 'light_pole radius value cannot be empty.'
    assert 'pole_number' in lidar_cfg.keys(), 'light_pole number value cannot be empty.'
    lidar_x = float(lidar_cfg['x'])
    lidar_y = float(lidar_cfg['y'])
    lidar_z = float(lidar_cfg['z'])
    pcd_level = int(lidar_cfg['level'])
    lidar_type = int(lidar_cfg['lidar_type'])
    lidar_resolution = float(lidar_cfg['lidar_resolution'])
    lidar_range = float(lidar_cfg['lidar_range'])
    pole_x = float(lidar_cfg['pole_x'])
    pole_y = float(lidar_cfg['pole_y'])
    pole_z = float(lidar_cfg['pole_z'])
    pole_radius = float(lidar_cfg['pole_radius'])
    pole_number = str(lidar_cfg['pole_number'])
    pcd_output = str(lidar_cfg['pcdOut'])
    scene_name = str(lidar_cfg['sceneName'])
    list_data = list(lidar_cfg['lidar_list']) if isinstance(lidar_cfg['lidar_list'], list) else None
    if list_data is None:
        error_str = 'lidar_list is None'
        return make_response(error_str, 500)
    else:
        lidar_list = list_data
        if len(lidar_list) != lidar_type:
            error_str = 'The number of radar wire harnesses is inconsistent with the radar model'
            return make_response(error_str, 500)
    lidar_lines = lidar_type
    origin_position = np.array([lidar_x, lidar_y, lidar_z])
    light_pole_position = np.array([pole_x, pole_y, pole_z])
    save_folder = os.path.join(pcd_output, scene_name)
    Params = {
        'origin_position': origin_position,
        'light_pole_position': light_pole_position,
        'lidar_type': lidar_type,
        'lidar_resolution': lidar_resolution,
        'lidar_range': lidar_range,
        'lidar_list': lidar_list,
        'pcd_level': pcd_level,
        'pole_radius': pole_radius,
        'pole_number': pole_number,
        'save_folder': save_folder
    }
    if points_low is None:
        request_str = "Please upload the point cloud file first. If it has already been uploaded, \
                        please wait for the point cloud to load or use a smaller point cloud file"
        # print(request_str)
        return make_response(request_str, 500)
    else:
        # 开始仿真
        print("开始仿真", time.time())
        p = threading.Thread(target=run, args=(Params, ))
        p.start()
        print("结束仿真", time.time())

        return make_response('setPosition OK', 200)


@app.route('/getCount', methods=['Post'])
def getCount():
    data = {
        'count': str(count),
        'all': str(lidar_lines)
    }
    return make_response(data, 200)


def runstart():
    app.run(host=config_json["host"], port=config_json["py_prot"])


def httpserver():
    runstart()


if __name__ == '__main__':
    # config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'config.json')
    config_path = os.path.join(os.path.dirname(sys.executable), 'config.json')
    load_config(config_path)

    if config_json is not None:
        httpserver()
    else:
        print("config json is None !!!")


